/*
 * serialport.cpp
 *
 *  Created on: 2011-8-29
 *      Author: chenl
 *     Purpose:
 *        Note:
 */

#include "serialport.h"
#include "common_libnamespace.h"
#include "common_err.h"

#include <string.h>
#include <assert.h>
#ifndef WIN32
#include <unistd.h>  /* UNIX standard function definitions */
#include <fcntl.h>   /* File control definitions */
#include <errno.h>   /* Error number definitions */
#include <termios.h> /* POSIX terminal control definitions */
#else
#include <windows.h>
#endif

CHENLIB_NAMESPACE_BEGIN

#define __SET_BAUDRATE__(n) SET_BAUDRATE(nBaudrate, n); break

CSerialPort::CSerialPort()
{
	memset(&m_setting, 0, sizeof(m_setting));
}

CSerialPort::~CSerialPort()
{
	if (CConnect::enmConnStatus_Closed != GetConnectonStatus())
	{
		DisConnect();
	}
}

int32_t CSerialPort::Connect(int fd)
{
	int32_t ret = SetSerialPortSetting();
	if (0 > ret)
	{
		return E_INVALIDARG;
	}
#ifndef WIN32
	fd = open(m_setting.szPortName, O_RDWR | O_NOCTTY | O_NDELAY);
	if (-1 == fd)
	{
		return E_CONNECTIONFAILED;
	}
	else
	{
		fcntl(fd, F_SETFL, 0);
	}
#else
	fd = CreateFile(m_setting.szPortName, GENERIC_READ, 0, 0, OPEN_EXISTING,
			FILE_FLAG_OVERLAPPED, 0);

	if (fd == INVALID_HANDLE_VALUE)
	{
		return E_CONNECTIONFAILED;
	}

#endif
	SetFD(fd);
	return S_OK;
}


uint32_t CSerialPort::Recv(char *pszBuf, uint32_t uLen)
{
#ifndef WIN32
	int nRead = read(GetFD(), pszBuf, uLen);
	if (-1 == nRead)
	{
		SetError(errno);
	}
#else
	int nRead = ReadFile(GetFD(), pszBuf, uLen);
	if (-1 == nRead)
	{
		SetError(GetLastError());
	}
#endif
	return nRead;
}

uint32_t CSerialPort::Send(const char *pszBuf, uint32_t uLen)
{
#ifndef WIN32
	int nRead = write(GetFD(), pszBuf, uLen);
	if (-1 == nRead)
	{
		SetError(errno);
		return 0;
	}
#else
	int nRead = WriteFile(GetFD(), pszBuf, uLen);
	if (-1 == nRead)
	{
		SetError(GetLastError());
		return 0;
	}
#endif
	return nRead;
}

int32_t CSerialPort::SetSerialPortSetting()
{
#ifndef WIN32
	struct termios options = {0};
	tcgetattr(GetFD(), &options);
#endif

	int nBaudrate = 0;
	switch (m_setting.nBaudrate)
	{
	case 50:
		__SET_BAUDRATE__(50);
	case 75:
		__SET_BAUDRATE__(75);
	case 110:
		__SET_BAUDRATE__(110);
	case 134:
		__SET_BAUDRATE__(134);
	case 150:
		__SET_BAUDRATE__(150);
	case 200:
		__SET_BAUDRATE__(200);
	case 300:
		__SET_BAUDRATE__(300);
	case 600:
		__SET_BAUDRATE__(600);
	case 1200:
		__SET_BAUDRATE__(1200);
	case 1800:
		__SET_BAUDRATE__(1800);
	case 2400:
		__SET_BAUDRATE__(2400);
	case 4800:
		__SET_BAUDRATE__(4800);
	case 9600:
		__SET_BAUDRATE__(9600);
	case 19200:
		__SET_BAUDRATE__(19200);
	case 38400:
		__SET_BAUDRATE__(38400);
	case 57600:
		__SET_BAUDRATE__(57600);
//	case 76800:
//		__SET_BAUDRATE__(76800);
	case 115200:
		__SET_BAUDRATE__(115200);
	case 0:
	default:
		__SET_BAUDRATE__(0);
	}

#ifndef WIN32
	//baud rate
	cfsetispeed(&options, nBaudrate);
	cfsetospeed(&options, nBaudrate);

	switch (m_setting.chParity)
	{
	//No parity (8N1):
	case 'N':
		options.c_cflag &= ~PARENB;
		options.c_cflag &= ~CSTOPB;
		options.c_cflag &= ~CSIZE;
		options.c_cflag |= CS8;
		break;
	//Even parity (7E1):
	case 'E':
		options.c_cflag |= PARENB;
		options.c_cflag &= ~PARODD;
		options.c_cflag &= ~CSTOPB;
		options.c_cflag &= ~CSIZE;
		options.c_cflag |= CS7;
		break;
	//Odd parity (7O1):
	case 'O':
		options.c_cflag |= PARENB;
		options.c_cflag |= PARODD;
		options.c_cflag &= ~CSTOPB;
		options.c_cflag &= ~CSIZE;
		options.c_cflag |= CS7;
		break;
	//Space parity is setup the same as no parity (7S1):
	case 'S':
		options.c_cflag &= ~PARENB;
		options.c_cflag &= ~CSTOPB;
		options.c_cflag &= ~CSIZE;
		options.c_cflag |= CS8;
		break;
	}
	//timeout

	/*
	 * Enable the receiver and set local mode...
	 */

	options.c_cflag |= (CLOCAL | CREAD);

	/*
	 * Set the new options for the port...
	 */

	tcsetattr(GetFD(), TCSANOW, &options);
#else
	DCB dcb;


	//baud rate
	dcb.BaudRate = nBaudrate;

	switch (m_setting.chParity)
	{
	//No parity (8N1):
	case 'N':
		dcb.ByteSize = 8;
		dcb.Parity = NOPARITY;
		dcb.StopBits = ONESTOPBIT;
		break;
	//Even parity (7E1):
	case 'E':
		dcb.ByteSize = 7;
		dcb.Parity = ODDPARITY;
		dcb.StopBits = ONESTOPBIT;
		break;
	//Odd parity (7O1):
	case 'O':
		dcb.ByteSize = 7;
		dcb.Parity = EVENPARITY;
		dcb.StopBits = ONESTOPBIT;
		break;
	//Space parity is setup the same as no parity (7S1):
	case 'S':
		dcb.ByteSize = 8;
		dcb.Parity = SPACEPARITY;
		dcb.StopBits = ONESTOPBIT;
		break;
	}
	//timeout
	ret = SetCommTimeouts(GetFD(), &m_setting.nTimeout);
	if (0 > ret)
	{
		SetError(GetLastError());
		return ret;
	}
	ret = SetCommState(GetFD(), &dcb);
	if (0 > ret)
	{
		SetError(GetLastError());
		return ret;
	}
#endif
	return S_OK;
}

CHENLIB_NAMESPACE_END
